package ast;

import java.util.ArrayList;

public class SimpleVisitor implements Visitor {

	private SymbolTable currTable = new SymbolTable("Global");
	private String tableValue;
	private ArrayList<String> classList = new ArrayList<String>();


    public Object visit (ActualParamList n) {
		if (n.left != null) n.left.accept(this);
		if (n.right != null) {n.right.accept(this);}
		return null;
	}

    public Object visit (AddNode n) {
    	n.left.accept(this);
    	System.out.print(" + ");
    	n.right.accept(this);
    	return null;
    }

    public Object visit (AndNode n) {
    	n.left.accept(this);
    	System.out.print(" && ");
    	n.right.accept(this);
    	return null;
    }

    public Object visit (AssignmentNode n) {
    	n.left.accept(this);
    	System.out.print(" = ");
    	n.right.accept(this);
    	System.out.println(";");
    	return null;
    }

    public Object visit (BasicTypeDecNode n) {
		System.out.print(n.type + " ");
		System.out.print(n.id);
    	if (n.right != null) {System.out.print(" " + n.assign + " "); n.right.accept(this);}
    	tableValue = ":" + n.type + ":" + "false";
    	currTable.put(n.id, tableValue);
    	System.out.println(";");
    	return n.id;
    }

    public Object visit (BodyNode n) {
    	System.out.println(n.lBrace);
    	if (n.left != null) {n.left.accept(this);}
		if (n.right != null) {n.right.accept(this);}
    	System.out.println(n.rBrace);
    	return null;
    }

    public Object visit (CallVarNode n) {
    	if (n.left != null) n.left.accept(this);
    	System.out.print(n.dot);
    	System.out.print(n.id);
    	System.out.print("(");
    	if (n.right != null) n.right.accept(this);
    	System.out.print(")");
    	return null;
    }

    public Object visit (ClassDecNode n) {
    	if (!classList.contains(n.id)) {
    		classList.add(n.id);
    		currTable = currTable.beginScope(n.id);
    		System.out.print("class ");
    		System.out.print(n.id);
    		System.out.println(" {");
    		if (n.left != null) n.left.accept(this);
    		System.out.println("}");
    		System.out.println();
    		currTable = currTable.endScope();
    	} else {
    		System.out.println("Error! Class name " + n.id + " has already been declared.");
    	}
    	return null;
    }

    public Object visit (ClassDefNode n) {
    	if (n.left != null) n.left.accept(this);
    	if (n.right != null) n.right.accept(this);
    	return null;
    }

    public Object visit (DivideNode n) {
    	n.left.accept(this);
    	System.out.print(" / ");
    	n.right.accept(this);
    	return null;
    }

    public Object visit (EqualNode n) {
    	n.left.accept(this);
    	System.out.print(" == ");
    	n.right.accept(this);
    	return null;
    }

    public Object visit (ExprNode n) {
        //n.left.accept(this);
    	//n.right.accept(this);
    	return null;
    }

    public Object visit (FalseNode n) {
        System.out.print(n.value);
        return null;
    }

    public Object visit (GreaterEqualNode n) {
    	n.left.accept(this);
    	System.out.print(" >= ");
    	n.right.accept(this);
    	return null;
    }

    public Object visit (GreaterNode n) {
    	n.left.accept(this);
    	System.out.print(" > ");
    	n.right.accept(this);
    	return null;
    }

    public Object visit (IfNode n) { //new scope
    	System.out.print("if ");
    	System.out.print("(");
    	n.expr.accept(this);
    	System.out.print(") ");
    	currTable = currTable.beginScope("if_body");
    	n.ifBody.accept(this);
    	currTable = currTable.endScope();
    	System.out.print("else ");
    	currTable = currTable.beginScope("else_body");
    	n.elseBody.accept(this);
    	currTable = currTable.endScope();
    	return null;
    }

    public Object visit (InNode n) {
    	System.out.print("(");
        n.left.accept(this);
    	System.out.print(" in ");
    	n.right.accept(this);
        System.out.print(")");
    	return null;
    }

    public Object visit (InstVarDecNode n) {
    	String key;
    	String modifier;

    	System.out.print(n.modifier + " ");
    	key = (String)n.right.accept(this);
    	if (n.modifier.equals("public")){
    		modifier = "true";
    	}
    	else{
    		modifier = "false";
    	}
    	currTable.editVar(key, modifier);
    	return null;
    }

    public Object visit (IntNumNode n) {
    	System.out.print(n.value.intValue());
    	return null;
    }

    public Object visit(LengthNode n) {
        System.out.print("len");
        System.out.print("(");
    	n.left.accept(this);
    	System.out.print(")");
        return null;
    }

    public Object visit (LessEqualNode n) {
    	n.left.accept(this);
    	System.out.print(" <= ");
    	n.right.accept(this);
    	return null;
    }

    public Object visit (LessNode n) {
    	n.left.accept(this);
    	System.out.print(" < ");
    	n.right.accept(this);
    	return null;
    }

	public Object visit (List n) {
		if (n.left != null) n.left.accept(this);
		if (n.right != null) {n.right.accept(this);}
		return null;
	}

    public Object visit (MainProgramNode n) {
    	currTable = currTable.beginScope("start");
    	System.out.print("start ");
    	System.out.println("{");
    	if (n.left != null) {n.left.accept(this);}
    	if (n.right != null) {n.right.accept(this);}
    	System.out.println("}");
        System.out.println();
        currTable = currTable.endScope();
    	return null;
    }

    public Object visit (MethodBodyNode n) {
    	System.out.println("{");
    	if (n.varDecList != null) {n.varDecList.accept(this);}
    	if (n.stmtList != null) {n.stmtList.accept(this);}
    	if (n.returnStmt != null) {n.returnStmt.accept(this);}
    	System.out.println("}");
        System.out.println();
    	return null;
    }

    public Object visit (MethodCallNode n) {
    	n.left.accept(this);
    	System.out.println(";");
    	return null;
    }

    public Object visit (MethodDecNode n) {//new scope
    	String concatType;
    	String modifier;

    	currTable = currTable.beginScope(n.id);
    	System.out.print(n.modifier + " ");
    	System.out.print(n.type + " ");
    	System.out.print(n.id);
    	System.out.print("(");
    	if (n.formalParam != null)
    	{
    		concatType = (String) n.formalParam.accept(this);
    	}
    	else
    	{
    		concatType = "void";
    	}
    	System.out.print(")");
    	n.methodBody.accept(this);
    	currTable = currTable.endScope();

    	if (n.modifier.equals("public")){
    		modifier = "true";
    	}
    	else{
    		modifier = "false";
    	}
    	tableValue = concatType + ":" + n.type + ":" + modifier;
    	currTable.put(n.id + "()", tableValue);
    	return null;
    }

    public Object visit (MinusNode n) {
    	n.left.accept(this);
    	System.out.print(" - ");
    	n.right.accept(this);
    	return null;
    }

    public Object visit (Node n) {
    	//n.left.accept(this);
    	//n.right.accept(this);
    	return null;
    }

    public Object visit (NotEqualNode n) {
    	n.left.accept(this);
    	System.out.print(" != ");
    	n.right.accept(this);
    	return null;
    }

    public Object visit (NotInNode n) {
    	System.out.print("(");
        n.left.accept(this);
    	System.out.print(" not in ");
    	n.right.accept(this);
        System.out.print(")");
    	return null;
    }

    public Object visit (NotNode n) {
    	System.out.print("!");
    	n.left.accept(this);
        return null;
    }

    public Object visit (ObjTypeDecNode n) {
    	System.out.print("newobj ");
		System.out.print(n.type + " ");
		System.out.print(n.id);
    	tableValue = ":" + n.type + ":" + "false";
    	currTable.put(n.id, tableValue);
		System.out.println(";");
    	return n.id;
    }

    public Object visit (OrNode n) {
    	n.left.accept(this);
    	System.out.print(" || ");
    	n.right.accept(this);
    	return null;
    }

    public Object visit (ParamListNode n) {
    	String concatType = "";
    	if (n.left != null)
    	{
    		concatType = (String)n.left.accept(this);
    	}
		if (n.right != null)
		{
			concatType = concatType + " x " + n.right.accept(this);
		}
		return concatType;
    }

    public Object visit (ParamNode n) {
    	System.out.print(n.type);
		System.out.print(n.id);
		tableValue = ":" + n.type + ":" + "false";
		currTable.put(n.id, tableValue);
        return n.type;
    }

    public Object visit (PowerNode n) {
    	n.left.accept(this);
    	System.out.print("^");
    	n.right.accept(this);
    	return null;
    }

    public Object visit (ProgramNode n) {
    	currTable.setParent(null);
    	if (n.left != null) {n.left.accept(this);}
    	n.right.accept(this);
    	currTable.loopList();
    	return currTable;
    }

    public Object visit (RealNumNode n) {
    	System.out.print(n.value.floatValue());
    	return null;
    }

    public Object visit (RepeatNode n) {//new scope
    	currTable = currTable.beginScope("repeat_body");
    	System.out.print("repeat ");
    	n.left.accept(this);
    	currTable = currTable.endScope();
    	System.out.print("until ");
    	System.out.print("(");
    	n.right.accept(this);
    	System.out.print(")");
    	System.out.println(";");
    	return null;
    }

    public Object visit (ReturnStmtNode n) {
    	System.out.print("return");
    	if (n.left != null) {System.out.print(" "); n.left.accept(this); }
    	System.out.println(";");
    	return null;
    }

    public Object visit (StmtNode n) {
    	//n.left.accept(this);
    	//n.right.accept(this);
    	return null;
    }

    public Object visit (StringNode n) {
    	System.out.print("\"" + n.value + "\"");
    	return null;
    }

    public Object visit (TimesNode n) {
    	n.left.accept(this);
    	System.out.print(" * ");
    	n.right.accept(this);
    	return null;
    }

    public Object visit (TrueNode n) {
        System.out.print(n.value);
        return null;
    }

    public Object visit (VarNode n) {
    	if (n.left != null) {n.left.accept(this);}
    	System.out.print(n.dot);
    	System.out.print(n.id);
    	return null;
    }

}